<!DOCTYPE html>


<html lang="zh_CN">
  

    <head>
      <meta charset="utf-8" />
        
      <meta
        name="viewport"
        content="width=device-width, initial-scale=1, maximum-scale=1"
      />
      <title>Redis |  MyBlog</title>
  <meta name="generator" content="hexo-theme-ayer">
      
      <link rel="shortcut icon" href="/favicon.ico" />
       
<link rel="stylesheet" href="/dist/main.css">

      <link
        rel="stylesheet"
        href="https://cdn.jsdelivr.net/gh/Shen-Yu/cdn/css/remixicon.min.css"
      />
      
<link rel="stylesheet" href="/css/custom.css">
 
      <script src="https://cdn.jsdelivr.net/npm/pace-js@1.0.2/pace.min.js"></script>
       
 

      <link
        rel="stylesheet"
        href="https://cdn.jsdelivr.net/npm/@sweetalert2/theme-bulma@5.0.1/bulma.min.css"
      />
      <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11.0.19/dist/sweetalert2.min.js"></script>

      <!-- mermaid -->
      
      <style>
        .swal2-styled.swal2-confirm {
          font-size: 1.6rem;
        }
      </style>
    <link rel="alternate" href="/atom.xml" title="MyBlog" type="application/atom+xml">
</head>
  </html>
</html>


<body>
  <div id="app">
    
      
    <main class="content on">
      <section class="outer">
  <article
  id="post-Redis笔记"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h1 class="article-title sea-center" style="border-left:0" itemprop="name">
  Redis
</h1>
 

      
    </header>
     
    <div class="article-meta">
      <a href="/2021/12/04/Redis%E7%AC%94%E8%AE%B0/" class="article-date">
  <time datetime="2021-12-04T12:33:56.098Z" itemprop="datePublished">2021-12-04</time>
</a>   
<div class="word_count">
    <span class="post-time">
        <span class="post-meta-item-icon">
            <i class="ri-quill-pen-line"></i>
            <span class="post-meta-item-text"> 字数统计:</span>
            <span class="post-count">10.7k</span>
        </span>
    </span>

    <span class="post-time">
        &nbsp; | &nbsp;
        <span class="post-meta-item-icon">
            <i class="ri-book-open-line"></i>
            <span class="post-meta-item-text"> 阅读时长≈</span>
            <span class="post-count">42 分钟</span>
        </span>
    </span>
</div>
 
    </div>
      
    <div class="tocbot"></div>




  
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="一、大数据时代的3V和3高"><a href="#一、大数据时代的3V和3高" class="headerlink" title="一、大数据时代的3V和3高"></a>一、大数据时代的3V和3高</h1><p><strong>3v：海量、实时、多样</strong></p>
<p><strong>3高：高并发、高扩张、高性能</strong></p>
<h1 id="二、NoSQL的四大分类"><a href="#二、NoSQL的四大分类" class="headerlink" title="二、NoSQL的四大分类"></a>二、NoSQL的四大分类</h1><p><strong>KV键值对：</strong></p>
<ul>
<li><strong>新浪：redis</strong></li>
<li><strong>美团：Redis + Tair</strong></li>
<li><strong>百度：Redis + memcache</strong></li>
</ul>
<p><strong>文档型数据库（bson格式和json一样）</strong></p>
<ul>
<li>MongDB（一般必须要掌握）<ul>
<li>MongDB是一个基于分布式文件存储的数据库，C++编写，主要用来处理大量文档</li>
<li>MongDB是一个介于关系型数据库和非关系型数据库的中间产品！MongDB是非关系型数据库中功能最丰富，最像关系型数据库</li>
</ul>
</li>
<li>ConthDB</li>
</ul>
<p><strong>列存储数据库</strong></p>
<ul>
<li>HBase</li>
<li>分布式文件类型</li>
</ul>
<p><strong>图型数据库</strong></p>
<blockquote>
<p>四者比较</p>
</blockquote>
<p><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111132203838.png"></p>
<h1 id="三、Redis入门"><a href="#三、Redis入门" class="headerlink" title="三、Redis入门"></a>三、Redis入门</h1><blockquote>
<p><strong>Redis启动和关闭常用命令</strong></p>
</blockquote>
<p><strong>服务端:</strong></p>
<p><strong>1、前台启动</strong></p>
<p>　　./redis-server (ctrl + c  退出前台启动)</p>
<p><strong>2、后台启动</strong></p>
<p>　　nohup ./redis-server ../redis.conf &amp;</p>
<p><strong>3、查看redis后台是否正常启动</strong></p>
<p>　　方式一:　　ps -ef |grep redis</p>
<p>　　方式二:　　ps aux | grep redis</p>
<p>　　方式三:　　客户端登录之后输入ping,如果能显示pong则代表后台启动成功.</p>
<p><strong>4关闭redis后台</strong></p>
<p>　　方式一: 先使用ps -ef|grep redis 或者是 ps aux|grep redis来查看redis的端口,然后输入kill redis端口号来关闭redis后台.</p>
<p>　　方式二: ./redis-cli shutdown</p>
<p><strong>客户端:</strong></p>
<p><strong>1、登录客户端</strong></p>
<p>　　./redis-cli -h 127.0.0.1 -p 6379</p>
<p>　　注: 客户端登录之后输入ping,如果能显示pong则代表后台启动成功.</p>
<p><strong>2、退出客户端</strong></p>
<p>　　方式一: ctrl + c</p>
<p>　　方式二: quit</p>
<p>　　方式三: exit</p>
<h2 id="（一）Redis概述"><a href="#（一）Redis概述" class="headerlink" title="（一）Redis概述"></a>（一）Redis概述</h2><blockquote>
<p>redis开启服务:</p>
</blockquote>
<p>redis-server: 默认前台开启，需要将redis.conf中的daemonize设置为yes才能开启后台服务——redis-server /配置文件坐在的位置</p>
<blockquote>
<p>redis开启客户端连接</p>
</blockquote>
<p>redis-cli -p 端口号（默认6379）</p>
<blockquote>
<p>redis功能·：</p>
</blockquote>
<p>1.内存存储、持久化、内存中是断电即失、所以需要持久化（RDB、AOF）</p>
<p>2.效率高，可以用于高速缓存</p>
<p>3.发布订阅系统</p>
<p>4.地图信息分析</p>
<p>5.计时器、计数器</p>
<p>6…….</p>
<blockquote>
<p>特性</p>
</blockquote>
<p>1.多样的数据类型</p>
<p>2.持久化</p>
<p>3.集群</p>
<p>4.事务</p>
<p>….</p>
<p>中文网:  <a target="_blank" rel="noopener" href="http://www.redis.cn/">http://www.redis.cn/</a></p>
<h2 id="（二）测试性能"><a href="#（二）测试性能" class="headerlink" title="（二）测试性能"></a>（二）测试性能</h2><p><strong>redis-benchmark</strong> 是一个压力测试工具</p>
<p>官方自带的性能测试工具！</p>
<p>redis-benchmark命令参数</p>
<p><strong>语法</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">redis-benchmark [option] [option value]</span><br></pre></td></tr></table></figure>

<p><strong>参数详解</strong></p>
<p><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111132203101.png"></p>
<p>测试：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 测试： 100个并发测试  100000请求</span></span><br><span class="line">redis-benchmark -h localhost -p 6379 -c 100 -n 100000</span><br></pre></td></tr></table></figure>

<h2 id="（三）基本知识"><a href="#（三）基本知识" class="headerlink" title="（三）基本知识"></a>（三）基本知识</h2><p><strong>在redis.conf配置文件中</strong></p>
<p>1.daemonize: 守护进程，默认值为no，表示前台开启redis服务</p>
<p>2.redis默认有16个数据库</p>
<p><strong>默认使用第一个数据库0</strong></p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; ping  <span class="comment"># 如果服务器运作正常的话,会返回一个 PONG </span></span><br><span class="line">PONG</span><br><span class="line">127.0.0.1:6379&gt; DBSIZE <span class="comment"># 切换数据库</span></span><br><span class="line">(<span class="built_in">integer</span>) 8</span><br><span class="line">127.0.0.1:6379&gt; select 1  <span class="comment"># 查看DB大小</span></span><br><span class="line">OK</span><br><span class="line">127.0.0.1:6379[1]&gt;</span><br></pre></td></tr></table></figure>

<p>清空当前数据库 flushdb</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379[1]&gt; flushdb </span><br><span class="line">OK</span><br></pre></td></tr></table></figure>

<p>清空全部数据库 flushall</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379[1]&gt; flushAll</span><br><span class="line">OK</span><br></pre></td></tr></table></figure>

<blockquote>
<p>Redis是单线程的！</p>
</blockquote>
<p>redis单线程快的原因：</p>
<p>核心：redis是将所有的数据全部放到内存中的，所以说使用单线程去操作效率就是最高的，多线程（CPU上下文会切换：耗时），对于内存系统来说，如果没有上下文切换效率就是最高的！</p>
<h2 id="（四）Redis五大数据类型"><a href="#（四）Redis五大数据类型" class="headerlink" title="（四）Redis五大数据类型"></a>（四）Redis五大数据类型</h2><p><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111132203031.png"></p>
<h3 id="1-Redis中的键（key）"><a href="#1-Redis中的键（key）" class="headerlink" title="1.Redis中的键（key）"></a>1.Redis中的键（key）</h3><p>查看当前库所有key（匹配：keys *1）：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">keys *</span><br></pre></td></tr></table></figure>

<p>判断某个key是否存在：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">exists key</span><br></pre></td></tr></table></figure>

<p>查看你的key是什么类型：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">type</span> key</span><br></pre></td></tr></table></figure>

<p>删除指定的key数据：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">del key</span><br></pre></td></tr></table></figure>

<p>移除数据:</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">move key 1  <span class="comment"># 将key移到第二个数据库，0表示第一个数据库</span></span><br></pre></td></tr></table></figure>

<p>设置某个key过期的时间:</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">expire key seconds</span><br></pre></td></tr></table></figure>

<p>查看key什么时候过期:</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ttl key   <span class="comment"># -1表示永不过期，-2表示已过期</span></span><br></pre></td></tr></table></figure>

<p>根据value选择非阻塞删除：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 仅将keys从keyspace元数据中删除，真正的删除会在后续一部操作</span></span><br><span class="line">unlink key</span><br></pre></td></tr></table></figure>

<h3 id="2-数据类型——字符串（String）"><a href="#2-数据类型——字符串（String）" class="headerlink" title="2.数据类型——字符串（String）"></a>2.数据类型——字符串（String）</h3><h4 id="2-1简介"><a href="#2-1简介" class="headerlink" title="2.1简介"></a>2.1简介</h4><p>String是Redis最基本的类型，你可以理解成与Memcached一模一样的类型，一个key对应一个value。</p>
<p>String类型是<strong>二进制安全的</strong>。意味着Redis的string可以包含任何数据。比如jpg图片或者序列化的对象。</p>
<p>String类型是Redis最基本的数据类型，一个Redis中字符串value最多可以是<strong>512M</strong></p>
<h4 id="2-2常用命令"><a href="#2-2常用命令" class="headerlink" title="2.2常用命令"></a>2.2常用命令</h4><p>添加键值对</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">set</span> key value  <span class="comment">#如果key存在，则用新的值覆盖</span></span><br></pre></td></tr></table></figure>

<p>查询对应键值：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">get key</span><br></pre></td></tr></table></figure>

<p>将给定的value追加到原值的末尾：（若key不存在就创建）</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">append key value   <span class="comment"># 返回新值的长度</span></span><br></pre></td></tr></table></figure>

<p>获得值的长度：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">strlen key</span><br></pre></td></tr></table></figure>

<p>设置key及过期时间:</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">setex key seconds value  <span class="comment">#如果key存在就用新的value取代</span></span><br></pre></td></tr></table></figure>

<p>只有在key不存在时设置key的值：（分布式锁常用）</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">setnx key value  <span class="comment"># 不存在，创建key，存在则失败，并返回0</span></span><br></pre></td></tr></table></figure>

<p>将 key 中储存的数字值增1<br>只能对数字值操作，如果为空，新增值为1</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">incr key  <span class="comment"># 返回更新后的数值</span></span><br></pre></td></tr></table></figure>

<p>将 key 中储存的数字值减1<br>只能对数字值操作，如果为空，新增值为-1</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">decr key</span><br></pre></td></tr></table></figure>

<p>将key中储存的数字值增减，自定义步长：                                                                                                                                      </p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">incrby / decrby key 数字值</span><br></pre></td></tr></table></figure>

<p>获取key中某范围内的值【start，end】</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">getrange key start end  <span class="comment">#如果输入的end超出范围则默认最后一个元素</span></span><br></pre></td></tr></table></figure>

<p>用value覆写key所储存的字符串值，从起始位置开始(索引从0开始)：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">setrange key 起始位置 value</span><br></pre></td></tr></table></figure>

<p>替换某个字段的值</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">setrange key offset value  <span class="comment"># 把某一位置开始替换与value长度一样的字符</span></span><br></pre></td></tr></table></figure>

<p>设置多个键值对：（若key已存在，就会有新值替换旧值）</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mset [k1 v1] [k2 v2]...</span><br></pre></td></tr></table></figure>

<p>只有不存在才设置：(原子性的，要么一起成功，要么一起失败)</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">msetnx [k1 v1] [k2 v2]... </span><br></pre></td></tr></table></figure>

<p>设置对象：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mset user:1:name v1 user:1:age v2</span><br><span class="line"><span class="built_in">set</span> user:1 &#123;name:zhangsan,age:3&#125;  <span class="comment"># 设置user:1 对象 值为json字符来保存一个对象</span></span><br></pre></td></tr></table></figure>

<p>以新换旧，设置了新值同时获得旧值：（如果存在，先获得再设置）</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">getset key value</span><br></pre></td></tr></table></figure>

<p>String类似的使用场景：value除了是我们的字符串还可以是数字</p>
<ul>
<li>计数器</li>
<li>统计多单位的数量 uid</li>
<li>粉丝数</li>
<li>对象缓存存储</li>
</ul>
<h3 id="3-数据类型——列表"><a href="#3-数据类型——列表" class="headerlink" title="3.数据类型——列表"></a>3.数据类型——列表</h3><h4 id="3-1-简介"><a href="#3-1-简介" class="headerlink" title="3.1 简介"></a>3.1 简介</h4><p>单键多值</p>
<p>Redis 列表是简单的字符串列表，按照插入顺序排序。你可以添加一个元素到列表的头部（左边）或者尾部（右边）</p>
<p>它的底层实际是个<strong>双向链表</strong>，对两端的操作性能很高，通过索引下标的操作中间的节点性能会较差。</p>
<p><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111132224174.png" alt="在这里插入图片描述"></p>
<h4 id="3-2常用命令"><a href="#3-2常用命令" class="headerlink" title="3.2常用命令"></a>3.2常用命令</h4><p>从左边/右边插入一个或多个值：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">lpush / rpush key value value value...</span><br></pre></td></tr></table></figure>

<p>从左边/右边吐出一个值。值在键在，值光键亡：类似队列</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">lpop / rpop key</span><br></pre></td></tr></table></figure>

<p>按照索引下标获得元素（从左到右）：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">lrange key 起始索引 结束索引</span><br><span class="line">lrange key 0 -1  <span class="comment">#0表示左边第一个，-1表示右边第一个（获取两者之间的所有值）</span></span><br></pre></td></tr></table></figure>

<p>按照索引下标获得元素（从左到右）： 索引从0开始</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">lindex key 索引</span><br></pre></td></tr></table></figure>

<p>返回列表的长度：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">llen key</span><br></pre></td></tr></table></figure>

<p>移除指定的值：(取关功能)</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">lrem key count value  <span class="comment">#移除key中的value，并且只移除count个</span></span><br></pre></td></tr></table></figure>

<p>修剪（截断）并替换原列表：[start,stop]</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ltrim key start stop  <span class="comment">#从左边第start个索引开始截取到stop</span></span><br></pre></td></tr></table></figure>

<p>将列表中右边的元素移除并添加到左边：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rpoplpush <span class="built_in">source</span> destination  <span class="comment">#将source中右边的第一个元素移到destination中最左边</span></span><br></pre></td></tr></table></figure>

<p>将列表中指定下标的值替换为另外一个值，前提列表存在：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">lset key index value</span><br></pre></td></tr></table></figure>

<p>将某个具体的值插入到列表中“pivot”元素的前面或者后面</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">linsert key before|after pivot element</span><br></pre></td></tr></table></figure>

<blockquote>
<p>小结</p>
</blockquote>
<ul>
<li>list实际上是个双向链表</li>
<li>如果key不存在，创建新的链表</li>
<li>如果key存在，新增内容</li>
<li>如果移除了所有值，空链表，也代表不存在</li>
<li>在两边插入或改动值，效率最高！中间元素，相对来说效率会低一点</li>
</ul>
<h3 id="4-数据类型——Set集合-去重"><a href="#4-数据类型——Set集合-去重" class="headerlink" title="4.数据类型——Set集合(去重)"></a>4.数据类型——Set集合(去重)</h3><h4 id="4-1-简介"><a href="#4-1-简介" class="headerlink" title="4.1 简介"></a>4.1 简介</h4><p>Redis set对外提供的功能与list类似是一个列表的功能，特殊之处在于set是可以自动排重的，当你需要存储一个列表数据，又不希望出现重复数据时，set是一个很好的选择，并且set提供了判断某个成员是否在一个set集合内的重要接口，这个也是list所不能提供的。</p>
<p>Redis的Set是string类型的无序集合。它底层其实是一个value为null的hash表，所以添加，删除，查找的**复杂度都是O(1)**。</p>
<p>一个算法，随着数据的增加，执行时间的长短，如果是O(1)，数据增加，查找数据的时间不变</p>
<h4 id="4-2-常用命令"><a href="#4-2-常用命令" class="headerlink" title="4.2 常用命令"></a>4.2 常用命令</h4><p>将一个或多个member元素加入到集合key中，已经存在的member元素将被忽略</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sadd key value1 value2...</span><br></pre></td></tr></table></figure>

<p>取出该集合的所有值：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">smembers key</span><br></pre></td></tr></table></figure>

<p>判断集合key是否为含有该value值，有1，没有0：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sismember key value</span><br></pre></td></tr></table></figure>

<p>返回该集合的元素个数：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">scard key</span><br></pre></td></tr></table></figure>

<p>删除集合中的某个元素：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">srem key value1 value2...</span><br></pre></td></tr></table></figure>

<p>随机从该集合中吐出一个值：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">spop key</span><br></pre></td></tr></table></figure>

<p>随机从该集合中取出n个值。不会从集合中删除：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">srandmember key n</span><br></pre></td></tr></table></figure>

<p>把集合中一个值从一个集合移动到另一个集合：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">smove 集合1 集合2 value</span><br></pre></td></tr></table></figure>

<p>返回两个集合的交集元素：（共同好友功能）</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sinter key1 key2</span><br></pre></td></tr></table></figure>

<p>返回两个集合的并集元素：(微博、B站共同关注)</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sunion key1 key2</span><br></pre></td></tr></table></figure>

<p>返回两个集合的差集元素(key1中的，不包含key2中的)：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sdiff key1 key2</span><br></pre></td></tr></table></figure>

<h4 id="4-3-数据结构"><a href="#4-3-数据结构" class="headerlink" title="4.3 数据结构"></a>4.3 数据结构</h4><p>Set数据结构是dict字典，字典是用哈希表实现的。</p>
<p>Java中HashSet的内部实现使用的是HashMap，只不过所有的value都指向同一个对象。Redis的set结构也是一样，它的内部也使用hash结构，所有的value都指向同一个内部值。</p>
<h3 id="5-数据类型—哈希（Hash）"><a href="#5-数据类型—哈希（Hash）" class="headerlink" title="5.数据类型—哈希（Hash）"></a>5.数据类型—哈希（Hash）</h3><h4 id="5-1-简介"><a href="#5-1-简介" class="headerlink" title="5.1 简介"></a>5.1 简介</h4><p>Redis hash 是一个键值对集合。</p>
<p>Redis hash是一个string类型的field和value的映射表，hash特别适合用于存储对象。</p>
<p>类似Java里面的Map&lt;String,Object&gt;</p>
<p>用户ID为查找的key，存储的value用户对象包含姓名，年龄，生日等信息，如果用普通的key/value结构来存储</p>
<p>主要有以下2种存储方式：<br><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111132345133.png" alt="image-20211113234511261"></p>
<h4 id="5-2-常用命令"><a href="#5-2-常用命令" class="headerlink" title="5.2 常用命令"></a>5.2 常用命令</h4><p>给key集合中的field键赋值value：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hset key field value</span><br></pre></td></tr></table></figure>

<p>从key1集合field取出 value ：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hget key1 field</span><br></pre></td></tr></table></figure>

<p>批量设置hash的值：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hmset key field1 value1 field2 value2...</span><br></pre></td></tr></table></figure>

<p>批量获取hash的值：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hmget key field1 field2 field3...</span><br></pre></td></tr></table></figure>

<p>查看哈希表 key 中，给定域 field 是否存在：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexists key1 field</span><br></pre></td></tr></table></figure>

<p>列出该hash集合的所有field：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hkeys key</span><br></pre></td></tr></table></figure>

<p>列出该hash集合的所有value：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hvals key</span><br></pre></td></tr></table></figure>

<p>为哈希表 key 中的域 field 的值加上增量 1 -1</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hincrby key field 增量</span><br></pre></td></tr></table></figure>

<p>将哈希表 key 中的域 field 的值设置为 value ，当且仅当域 field 不存在：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hsetnx key field value</span><br></pre></td></tr></table></figure>

<h4 id="5-3-数据结构"><a href="#5-3-数据结构" class="headerlink" title="5.3 数据结构"></a>5.3 数据结构</h4><p>Hash类型对应的数据结构是两种：ziplist（压缩列表），hashtable（哈希表）。当field-value长度较短且个数较少时，使用ziplist，否则使用hashtable。</p>
<h4 id="5-4应用场景"><a href="#5-4应用场景" class="headerlink" title="5.4应用场景"></a>5.4应用场景</h4><p>变更的数据，尤其是用户信息之类的，经常变动的信息！hash适合对象的存储，String更加适合字符串存储</p>
<h3 id="6-数据类型—有序集合（Zset）"><a href="#6-数据类型—有序集合（Zset）" class="headerlink" title="6.数据类型—有序集合（Zset）"></a>6.数据类型—有序集合（Zset）</h3><h4 id="6-1-简介"><a href="#6-1-简介" class="headerlink" title="6.1 简介"></a>6.1 简介</h4><p>Redis有序集合zset与普通集合set非常相似，是一个没有重复元素的字符串集合。</p>
<p>不同之处是有序集合的每个成员都关联了一个<strong>评分（score）</strong>,这个评分（score）被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的，但是评分可以是重复了 。</p>
<p>因为元素是有序的, 所以你也可以很快的根据评分（score）或者次序（position）来获取一个范围的元素。</p>
<p>访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的智能列表。</p>
<h4 id="6-2-常用命令"><a href="#6-2-常用命令" class="headerlink" title="6.2 常用命令"></a>6.2 常用命令</h4><p>将一个或多个 member 元素及其 score 值加入到有序集 key 当中：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">zadd key score1 value1 score2 value2</span><br></pre></td></tr></table></figure>

<p>返回有序集 key 中，下标在start、stop之间的元素 (-inf：负无穷，+inf：正无穷)<br>带WITHSCORES，可以让分数一起和值返回到结果集：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">zrange key start stop [withscores] # 从小到大排</span><br></pre></td></tr></table></figure>

<p>根据评分从大到小排序：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">zrevrange key start stop [withscores] # 从大到小排</span><br></pre></td></tr></table></figure>

<p>返回有序集 key 中，所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">zrangebyscore key min max [withscores] [limit offset count]</span><br></pre></td></tr></table></figure>

<p>同上，改为从大到小排列：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">zrevrangebyscore key max min [withscores] [limit offset count]  </span><br></pre></td></tr></table></figure>

<p>为元素的score加上增量：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">zincrby key 增量 value</span><br></pre></td></tr></table></figure>

<p>删除该集合下，指定值的元素：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">zrem key value</span><br></pre></td></tr></table></figure>

<p>统计该集合，分数区间内的元素个数：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">zcount key min max </span><br></pre></td></tr></table></figure>

<p>返回该值在集合中的排名，从0开始：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">zrank key value</span><br></pre></td></tr></table></figure>

<p>统计key中的元素个数：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">zcard key</span><br></pre></td></tr></table></figure>

<h5 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h5><p>普通消息为 1，重要消息为 2，带权重进行判断</p>
<h4 id="6-3-数据结构"><a href="#6-3-数据结构" class="headerlink" title="6.3 数据结构"></a>6.3 数据结构</h4><p>SortedSet(zset)是Redis提供的一个非常特别的数据结构，一方面它等价于Java的数据结构Map&lt;String, Double&gt;，可以给每一个元素value赋予一个权重score，另一方面它又类似于TreeSet，内部的元素会按照权重score进行排序，可以得到每个元素的名次，还可以通过score的范围来获取元素的列表。</p>
<p>zset底层使用了两个数据结构：</p>
<ol>
<li>hash，hash的作用就是关联元素value和权重score，保障元素value的唯一性，可以通过元素value找到相应的score值。</li>
<li>跳跃表，跳跃表的目的在于给元素value排序，根据score的范围获取元素列表。</li>
</ol>
<h4 id="6-4-跳跃表（跳表）"><a href="#6-4-跳跃表（跳表）" class="headerlink" title="6.4 跳跃表（跳表）"></a>6.4 跳跃表（跳表）</h4><ol>
<li>简介：<br>有序集合在生活中比较常见，例如根据成绩对学生排名，根据得分对玩家排名等。对于有序集合的底层实现，可以用数组、平衡树、链表等。数组不便元素的插入、删除；平衡树或红黑树虽然效率高但结构复杂；链表查询需要遍历所有效率低。Redis采用的是跳跃表。跳跃表效率堪比红黑树，实现远比红黑树简单。</li>
</ol>
<hr>
<p>​    2.实例：</p>
<p>​        对比有序链表和跳跃表，从链表中查询出51</p>
<blockquote>
<p>有序链表 <img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111140016799.png" alt="在这里插入图片描述"><br>要查找值为51的元素，需要从第一个元素开始依次查找、比较才能找到。共需要6次比较。</p>
<p>跳跃表<br><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111140017600.png" alt="在这里插入图片描述"><br>从第2层开始，1节点比51节点小，向后比较。<br>21节点比51节点小，继续向后比较，后面就是NULL了，所以从21节点向下到第1层<br>在第1层，41节点比51节点小，继续向后，61节点比51节点大，所以从41向下<br>在第0层，51节点为要查找的节点，节点被找到，共查找4次。<br>从此可以看出跳跃表比有序链表效率要高</p>
</blockquote>
<h3 id="补充"><a href="#补充" class="headerlink" title="补充"></a>补充</h3><p>重命名key</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rename key newKey  </span><br></pre></td></tr></table></figure>

<p>随机返回数据库的某个key</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">randomkey</span><br></pre></td></tr></table></figure>



<h2 id="（五）三种特殊数据类型"><a href="#（五）三种特殊数据类型" class="headerlink" title="（五）三种特殊数据类型"></a>（五）三种特殊数据类型</h2><h3 id="1-Geospatial-地理位置"><a href="#1-Geospatial-地理位置" class="headerlink" title="1.Geospatial 地理位置"></a>1.Geospatial 地理位置</h3><h4 id="1-1-简介"><a href="#1-1-简介" class="headerlink" title="1.1 简介"></a>1.1 简介</h4><p>Redis 3.2 中增加了对GEO类型的支持。GEO，Geographic，地理信息的缩写。该类型，就是元素的2维坐标，在地图上就是经纬度。redis基于该类型，提供了经纬度设置，查询，范围查询，距离查询，经纬度Hash等常见操作。</p>
<p>查询一些测试数据：<a target="_blank" rel="noopener" href="http://www.jsons.cn/lngcode">http://www.jsons.cn/lngcode</a></p>
<p><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111141434383.png" alt="image-20211114143454347"></p>
<h4 id="1-2-常用命令"><a href="#1-2-常用命令" class="headerlink" title="1.2 常用命令"></a>1.2 常用命令</h4><blockquote>
<h5 id="1-geoadd"><a href="#1-geoadd" class="headerlink" title="1.geoadd"></a>1.geoadd</h5></blockquote>
<p>添加地理位置（经度，纬度，名称）：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">geoadd key longitude latitude member [longitude latitude member...]</span><br></pre></td></tr></table></figure>

<p>实例：<br>geoadd china:city 121.47 31.23 shanghai<br>geoadd china:city 106.50 29.53 chongqing 114.05 22.52 shenzhen 116.38 39.90 beijing<br><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111141445138.png" alt="image-20211114144531326"></p>
<p>两极无法直接添加，一般会下载城市数据，直接通过 Java 程序一次性导入。</p>
<p><strong>有效的经度从 -180 度到 180 度。有效的纬度从 -85.05112878 度到 85.05112878 度。</strong><br>当坐标位置超出指定范围时，该命令将会返回一个错误。<br>已经添加的数据，是无法再次往里面添加的。</p>
<hr>
<blockquote>
<h5 id="2-geopos"><a href="#2-geopos" class="headerlink" title="2.geopos"></a>2.geopos</h5></blockquote>
<p>获得指定地区的坐标值：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">geopos key member [member...]  </span><br></pre></td></tr></table></figure>

<p>实例：<br><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111141449518.png" alt="image-20211114144949980"></p>
<hr>
<blockquote>
<h5 id="3-geodist"><a href="#3-geodist" class="headerlink" title="3.geodist"></a>3.geodist</h5></blockquote>
<p><strong>获取两个位置之间的直线距离</strong>：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">geodist key member1 member2 [m|km|ft|mi] </span><br></pre></td></tr></table></figure>

<p>实例：</p>
<p>获取两个位置之间的直线距离<br><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111141452971.png" alt="image-20211114145212527"><br>单位：</p>
<ul>
<li>m 表示单位为米**[默认值]**。</li>
<li>km 表示单位为千米。</li>
<li>mi 表示单位为英里。</li>
<li>ft 表示单位为英尺。</li>
<li>如果用户没有显式地指定单位参数， 那么 <strong>GEODIST 默认使用m作为单位</strong></li>
</ul>
<blockquote>
<p>应用场景</p>
</blockquote>
<p>获得所有附近的人的地址，定位</p>
<hr>
<blockquote>
<h5 id="4-georadius"><a href="#4-georadius" class="headerlink" title="4.georadius"></a>4.georadius</h5></blockquote>
<p>以给定的经纬度为中心，找出某一半径内的元素：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"> 经度 纬度 距离 单位 显示经度纬度</span><br><span class="line">georadius key longitude latitude radius  m|km|ft|mi [withcoord]</span><br></pre></td></tr></table></figure>

<p>实例：<br><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111141459933.png" alt="在这里插入图片描述"></p>
<p>可选参数：</p>
<ul>
<li><code>WITHDIST</code>: 在返回位置元素的同时， 将位置元素与中心之间的距离也一并返回。 距离的单位和用户给定的范围单位保持一致。</li>
<li><code>WITHCOORD</code>: 将位置元素的经度和维度也一并返回。</li>
<li><code>WITHHASH</code>: 以 52 位有符号整数的形式， 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试， 实际中的作用并不大。</li>
</ul>
<blockquote>
<h5 id="5-georadiusbymember"><a href="#5-georadiusbymember" class="headerlink" title="5.georadiusbymember"></a>5.georadiusbymember</h5></blockquote>
<p>这个命令和 <a target="_blank" rel="noopener" href="http://www.redis.cn/commands/georadius.html">GEORADIUS</a> 命令一样， 都可以找出位于指定范围内的元素， 但是 <code>GEORADIUSBYMEMBER</code> 的中心点是由给定的位置元素决定的， 而不是像 <a target="_blank" rel="noopener" href="http://www.redis.cn/commands/georadius.html">GEORADIUS</a> 那样， 使用输入的经度和纬度来决定中心点</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">redis&gt; GEOADD Sicily 13.583333 37.316667 <span class="string">&quot;Agrigento&quot;</span></span><br><span class="line">(<span class="built_in">integer</span>) 1</span><br><span class="line">redis&gt; GEOADD Sicily 13.361389 38.115556 <span class="string">&quot;Palermo&quot;</span> 15.087269 37.502669 <span class="string">&quot;Catania&quot;</span></span><br><span class="line">(<span class="built_in">integer</span>) 2</span><br><span class="line">redis&gt; GEORADIUSBYMEMBER Sicily Agrigento 100 km</span><br><span class="line">1) <span class="string">&quot;Agrigento&quot;</span></span><br><span class="line">2) <span class="string">&quot;Palermo&quot;</span></span><br><span class="line">redis&gt; </span><br></pre></td></tr></table></figure>

<blockquote>
<h5 id="6-geohash"><a href="#6-geohash" class="headerlink" title="6.geohash"></a>6.geohash</h5></blockquote>
<p>返回一个或多个位置元素的 <a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Geohash">Geohash</a> 表示。</p>
<p>该命令将返回11个字符的Geohash字符串，所以没有精度Geohash，损失相比，使用内部52位表示</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#将二维的经纬度转换为一维的字符串，如果两个字符串越接近，那么距离就越近</span></span><br><span class="line">127.0.0.1:6379&gt; geohash china:city 北京 重庆</span><br><span class="line">1) <span class="string">&quot;wx4fbxxfke0&quot;</span></span><br><span class="line">2) <span class="string">&quot;wm5xzrybty0&quot;</span></span><br></pre></td></tr></table></figure>

<blockquote>
<p>Geo底层实现原理其实就是Zset！所以可以使用Zset操作geo</p>
</blockquote>
<h3 id="2-Hyperloglog"><a href="#2-Hyperloglog" class="headerlink" title="2.Hyperloglog"></a>2.Hyperloglog</h3><blockquote>
<p>什么是基数？  一个集合中不重复元素的个数</p>
</blockquote>
<p>比如数据集 {1, 3, 5, 7, 5, 7, 8}， 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内，快速计算基数。</p>
<h4 id="2-1简介-1"><a href="#2-1简介-1" class="headerlink" title="2.1简介"></a>2.1简介</h4><p>Redis 2.8.9 版本就更新了Hyperloglog数据结构</p>
<p>Redis Hyperloglog 基数统计的算法！</p>
<p>优点：占用的内存是固定的，2^64不同的元素的基数，只需要废12KB内存！如果要从内存角度来的华Hyperloglog首选</p>
<blockquote>
<p>应用场景</p>
</blockquote>
<p>在工作当中，我们经常会遇到与统计相关的功能需求，比如统计网站PV（PageView页面访问量）,可以使用Redis的incr、incrby轻松实现。</p>
<p>但像UV（UniqueVisitor，独立访客）、独立IP数、搜索记录数等需要去重和计数的问题如何解决？这种求集合中<strong>不重复</strong>元素个数的问题称为基数问题。</p>
<p>（一个人访问一个网站多次，但是还是算作一个人！）</p>
<ul>
<li>传统方式，set保存用户的id，然后就可以统计set中的元素数量作为标准判断！ 缺陷：需要保存大量用户id</li>
</ul>
<h4 id="2-2常用命令-1"><a href="#2-2常用命令-1" class="headerlink" title="2.2常用命令"></a>2.2常用命令</h4><blockquote>
<p>1.pfadd</p>
</blockquote>
<p>添加指定元素到 HyperLogLog 中：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pfadd key element element...</span><br></pre></td></tr></table></figure>

<p>实例：<br><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111141541894.png" alt="image-20211114154100630"><br>将所有元素添加到指定HyperLogLog数据结构中。如果执行命令后HLL估计的近似基数发生变化，则返回1，否则返回0。</p>
<hr>
<blockquote>
<p>2.pfcount</p>
</blockquote>
<p>统计一个集合中不重复的元素个数：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pfcount key [key...]</span><br></pre></td></tr></table></figure>

<p>实例：<br><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111141543357.png" alt="image-20211114154137679"></p>
<hr>
<blockquote>
<p>3.pfmerge</p>
</blockquote>
<p>取两个集合的并集：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pfmerge destkey sourcekey [sourcekey ...]</span><br></pre></td></tr></table></figure>

<p>实例：<br><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111141543740.png" alt="image-20211114154221171"></p>
<p>如果允许容错，那么一定可以使用Hyperloglog</p>
<p>如果不允许容错，就使用set或者自己的数据类型即可</p>
<h3 id="3-Bitmaps"><a href="#3-Bitmaps" class="headerlink" title="3.Bitmaps"></a>3.Bitmaps</h3><blockquote>
<p>位存储</p>
</blockquote>
<p>统计用户信息，活跃，不活跃！登录、未登录！打卡，365打卡！ 两个状态的，都可以使用Bitmaps！</p>
<p>Bitmaps位图，数据结构！都是操作二进制为来进行记录，就只有0和1两个状态！</p>
<p>365天 = 365bit， 1字节 = 8 bit   46个字节左右</p>
<blockquote>
<p>常用命令</p>
</blockquote>
<p><strong>设置一周的打卡情况：1表示打卡，0表示未打卡</strong></p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; setbit sign 0 1</span><br><span class="line">(<span class="built_in">integer</span>) 0</span><br><span class="line">127.0.0.1:6379&gt; setbit sign 1 0</span><br><span class="line">(<span class="built_in">integer</span>) 0</span><br><span class="line">127.0.0.1:6379&gt; setbit sign 2 1</span><br><span class="line">(<span class="built_in">integer</span>) 0</span><br><span class="line">127.0.0.1:6379&gt; setbit sign 3 0</span><br><span class="line">(<span class="built_in">integer</span>) 0</span><br><span class="line">127.0.0.1:6379&gt; setbit sign 4 0</span><br><span class="line">(<span class="built_in">integer</span>) 0</span><br><span class="line">127.0.0.1:6379&gt; setbit sign 5 1</span><br><span class="line">(<span class="built_in">integer</span>) 0</span><br><span class="line">127.0.0.1:6379&gt; setbit sign 6 1</span><br><span class="line">(<span class="built_in">integer</span>) 0</span><br></pre></td></tr></table></figure>

<p>查看某一天是否有打卡：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; getbit sign 4</span><br><span class="line">(<span class="built_in">integer</span>) 0</span><br><span class="line">127.0.0.1:6379&gt; getbit sign 6</span><br><span class="line">(<span class="built_in">integer</span>) 1</span><br></pre></td></tr></table></figure>

<p>统计操作，统计打卡的天数：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; bitcount sign</span><br><span class="line">(integer) 4</span><br></pre></td></tr></table></figure>

<p>bitop是一个复合操作， 它可以做多个Bitmaps的and（交集） 、 or（并集） 、 not（非） 、 xor（异或） 操作并将结果保存在destkey中。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bitop and(or/not/xor) &lt;destkey&gt; [key…]</span><br></pre></td></tr></table></figure>

<p><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111141607223.png" alt="在这里插入图片描述"></p>
<blockquote>
<p> <strong>Bitmaps与set对比</strong></p>
</blockquote>
<p>假设网站有1亿用户， 每天独立访问的用户有5千万， 如果每天用集合类型和Bitmaps分别存储活跃用户可以得到表<br><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111141608039.png" alt="在这里插入图片描述"><br>很明显， 这种情况下使用Bitmaps能节省很多的内存空间， 尤其是随着时间推移节省的内存还是非常可观的<br><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111141608078.png" alt="在这里插入图片描述"><br>但Bitmaps并不是万金油， 假如该网站每天的独立访问用户很少， 例如只有10万（大量的僵尸用户） ， 那么两者的对比如下表所示， 很显然， 这时候使用Bitmaps就不太合适了， 因为基本上大部分位都是0。<br><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111141608643.png" alt="在这里插入图片描述"></p>
<h3 id="六-事务"><a href="#六-事务" class="headerlink" title="(六)事务"></a>(六)事务</h3><p>Redis事务本质：一组命令的集合！一个事务中的所有命令都会被序列化，在事务执行过程中，会按照顺序执行！</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure>

<p>Redis事务没有隔离级别的概念</p>
<p>所有的命令在事务中，并没有直接被执行！只有发起执行命令的时候才会执行！</p>
<p>Redis单条命令式保存原子性的，但是事务不保证原子性</p>
<p>redis的事务：</p>
<ul>
<li>开启事务( Multi )</li>
<li>命令入队( ….. )</li>
<li>执行事务( exec  )</li>
</ul>
<blockquote>
<p>正常执行事务！</p>
</blockquote>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; multi  <span class="comment"># 开启事务</span></span><br><span class="line">OK</span><br><span class="line"><span class="comment"># 命令入队</span></span><br><span class="line">127.0.0.1:6379(TX)&gt; <span class="built_in">set</span> k1 v1 </span><br><span class="line">QUEUED</span><br><span class="line">127.0.0.1:6379(TX)&gt; <span class="built_in">set</span> k2 v2</span><br><span class="line">QUEUED</span><br><span class="line">127.0.0.1:6379(TX)&gt; get k2</span><br><span class="line">QUEUED</span><br><span class="line">127.0.0.1:6379(TX)&gt; <span class="built_in">set</span> k3 v3</span><br><span class="line">QUEUED</span><br><span class="line">127.0.0.1:6379(TX)&gt; <span class="built_in">exec</span>  <span class="comment"># 执行结果</span></span><br><span class="line">1) OK</span><br><span class="line">2) OK</span><br><span class="line">3) <span class="string">&quot;v2&quot;</span></span><br><span class="line">4) OK</span><br><span class="line">127.0.0.1:6379&gt; </span><br></pre></td></tr></table></figure>

<blockquote>
<p>放弃事务！</p>
</blockquote>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; multi  <span class="comment"># 开启事务</span></span><br><span class="line">OK</span><br><span class="line">127.0.0.1:6379(TX)&gt; <span class="built_in">set</span> k1 v1</span><br><span class="line">QUEUED</span><br><span class="line">127.0.0.1:6379(TX)&gt; <span class="built_in">set</span> k2 v2</span><br><span class="line">QUEUED</span><br><span class="line">127.0.0.1:6379(TX)&gt; <span class="built_in">set</span> k4 v4</span><br><span class="line">QUEUED</span><br><span class="line">127.0.0.1:6379(TX)&gt; DISCARD  <span class="comment"># 取消事务</span></span><br><span class="line">OK</span><br><span class="line">127.0.0.1:6379&gt; get k4 <span class="comment"># wu</span></span><br><span class="line">(nil)</span><br></pre></td></tr></table></figure>

<blockquote>
<p>编译型异常, 事务中的所有命令都不会执行</p>
</blockquote>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; multi</span><br><span class="line">OK</span><br><span class="line">127.0.0.1:6379(TX)&gt; <span class="built_in">set</span> k1 v1</span><br><span class="line">QUEUED</span><br><span class="line">127.0.0.1:6379(TX)&gt; <span class="built_in">set</span> k2 v2</span><br><span class="line">QUEUED</span><br><span class="line">127.0.0.1:6379(TX)&gt; <span class="built_in">set</span> k3 v3</span><br><span class="line">QUEUED</span><br><span class="line">127.0.0.1:6379(TX)&gt; getset k3  <span class="comment"># 错误的命令</span></span><br><span class="line">(error) ERR wrong number of arguments <span class="keyword">for</span> <span class="string">&#x27;getset&#x27;</span> <span class="built_in">command</span></span><br><span class="line">127.0.0.1:6379(TX)&gt; <span class="built_in">set</span> k4 v4</span><br><span class="line">QUEUED</span><br><span class="line">127.0.0.1:6379(TX)&gt; <span class="built_in">exec</span>  <span class="comment"># 执行事务报错</span></span><br><span class="line">(error) EXECABORT Transaction discarded because of previous errors.</span><br><span class="line">127.0.0.1:6379&gt; get k4  <span class="comment"># 所有的命令都不会执行</span></span><br><span class="line">(nil)</span><br></pre></td></tr></table></figure>

<blockquote>
<p>运行时异常， 如果事务队列中存在语法性，那么执行命令的时候，其他命令式可以正常执行，错误命令抛出异常</p>
</blockquote>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; <span class="built_in">set</span> k1 <span class="string">&quot;v1&quot;</span></span><br><span class="line">OK</span><br><span class="line">127.0.0.1:6379&gt; multi</span><br><span class="line">OK</span><br><span class="line">127.0.0.1:6379(TX)&gt; incr k1  <span class="comment"># 会执行的时候报错</span></span><br><span class="line">QUEUED</span><br><span class="line">127.0.0.1:6379(TX)&gt; <span class="built_in">set</span> k2 v2</span><br><span class="line">QUEUED</span><br><span class="line">127.0.0.1:6379(TX)&gt; <span class="built_in">set</span> k3 v3</span><br><span class="line">QUEUED</span><br><span class="line">127.0.0.1:6379(TX)&gt; <span class="built_in">exec</span></span><br><span class="line">1) (error) ERR value is not an <span class="built_in">integer</span> or out of range</span><br><span class="line">2) OK</span><br><span class="line">3) OK</span><br><span class="line">127.0.0.1:6379&gt; get k2</span><br><span class="line"><span class="string">&quot;v2&quot;</span></span><br></pre></td></tr></table></figure>

<blockquote>
<p>监控！</p>
</blockquote>
<p><strong>悲观锁：</strong></p>
<ul>
<li>很悲观，认为什么时候都会出问题，无论做什么都会加锁</li>
</ul>
<p><strong>乐观锁：</strong></p>
<ul>
<li><p>很乐观，认为什么时候都不会出问题，所有不会上锁！更新数据的时候去判断，在此期间是否有人修改过数据，</p>
</li>
<li><p>获得version</p>
</li>
<li><p>更新的时候比较version</p>
</li>
</ul>
<blockquote>
<p>Redis测监视</p>
</blockquote>
<p>正常执行成功！</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; <span class="built_in">set</span> money 100  </span><br><span class="line">OK</span><br><span class="line">127.0.0.1:6379&gt; <span class="built_in">set</span> out 0</span><br><span class="line">OK</span><br><span class="line">127.0.0.1:6379&gt; watch money  <span class="comment"># 监视 money 对象</span></span><br><span class="line">OK</span><br><span class="line">127.0.0.1:6379&gt; multi  <span class="comment"># 事务正常结束，数据期间没有发生变动，这个时候正常执行成功</span></span><br><span class="line">OK</span><br><span class="line">127.0.0.1:6379(TX)&gt; decrby money 20</span><br><span class="line">QUEUED</span><br><span class="line">127.0.0.1:6379(TX)&gt; INCRby out 20</span><br><span class="line">QUEUED</span><br><span class="line">127.0.0.1:6379(TX)&gt; <span class="built_in">exec</span></span><br><span class="line">1) (<span class="built_in">integer</span>) 80</span><br><span class="line">2) (<span class="built_in">integer</span>) 20</span><br></pre></td></tr></table></figure>

<p>测试多线程修改值，使用watch 可以当做redis的乐观锁操作！</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 会话1</span></span><br><span class="line">127.0.0.1:6379&gt; watch money  <span class="comment"># 监视 money</span></span><br><span class="line">OK</span><br><span class="line">127.0.0.1:6379&gt; multi</span><br><span class="line">OK</span><br><span class="line">127.0.0.1:6379(TX)&gt; decrby money 10</span><br><span class="line">QUEUED</span><br><span class="line">127.0.0.1:6379(TX)&gt; <span class="built_in">exec</span> <span class="comment"># 执行之前，另外一个线程对监视的对象进行了修改，导致事务执行失败</span></span><br><span class="line">(nil)</span><br></pre></td></tr></table></figure>

<p>另一线程</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 会话2</span></span><br><span class="line">127.0.0.1:6379&gt; get money</span><br><span class="line"><span class="string">&quot;80&quot;</span></span><br><span class="line">127.0.0.1:6379&gt; <span class="built_in">set</span> money 1000</span><br><span class="line">OK</span><br><span class="line">127.0.0.1:6379&gt; get money</span><br><span class="line"><span class="string">&quot;1000&quot;</span></span><br></pre></td></tr></table></figure>

<p>解锁</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; unwatch  <span class="comment"># 如果发现事务执行失败，就先解锁</span></span><br><span class="line">OK</span><br><span class="line">127.0.0.1:6379&gt; watch money <span class="comment"># 获得最新的值，再次监视</span></span><br><span class="line">OK</span><br><span class="line">127.0.0.1:6379&gt; multi</span><br><span class="line">OK</span><br><span class="line">127.0.0.1:6379(TX)&gt; decrby money 300</span><br><span class="line">QUEUED</span><br><span class="line">127.0.0.1:6379(TX)&gt; <span class="built_in">exec</span></span><br><span class="line">1) (<span class="built_in">integer</span>) 500</span><br></pre></td></tr></table></figure>

<h3 id="七-Jedis"><a href="#七-Jedis" class="headerlink" title="(七) Jedis"></a>(七) Jedis</h3><p>使用java操作redis</p>
<blockquote>
<p>什么是jedis —— 是redis官方推荐的java连接开发工具 ! 使用java操作redis的中间件</p>
</blockquote>
<blockquote>
<p>测试</p>
</blockquote>
<p>1.导入对应的依赖</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--导入jedis的包--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>redis.clients<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>jedis<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.2.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--fastjson--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.alibaba<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>fastjson<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.2.62<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>2.编码测试：</p>
<ul>
<li>连接数据库</li>
<li>操作命令</li>
<li>断开连接</li>
</ul>
<p><strong>注意：需要先将redis.conf中的bind 127.0.0.1注释掉以及protected mode 改为no</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestPing</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//创建Jedis</span></span><br><span class="line">        Jedis jedis = <span class="keyword">new</span> Jedis(<span class="string">&quot;192.168.227.20&quot;</span>,<span class="number">6379</span>);</span><br><span class="line">        <span class="comment">//jedis 所有的命令就是之前学过的所有指令</span></span><br><span class="line">        System.out.println(jedis.ping());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111162154769.png" alt="image-20211116215430837"></p>
<h4 id="常用的API"><a href="#常用的API" class="headerlink" title="常用的API"></a>常用的API</h4><p>String、List、Set、Hash、ZSet</p>
<h2 id="SpringBoot整合"><a href="#SpringBoot整合" class="headerlink" title="SpringBoot整合"></a>SpringBoot整合</h2><p>SpringBoot操作数据：spring-data</p>
<p>说明：在SpringBoot2.x之后，原来使用的jedis被替换为lettuce</p>
<p>jedis：采用的直连，多线程操作的话，是不安全的，如果想要避免不安全的，使用jedis pool连接池 BIO</p>
<p>lettuce：采用netty，实例可以再多个线程中进行共享，不存在线程不安全的情况！可以减少线程数据 NIO</p>
<p>源码分析：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Bean</span></span><br><span class="line">	<span class="meta">@ConditionalOnMissingBean(name = &quot;redisTemplate&quot;)</span> <span class="comment">// 可以自定义一个redisTemplate来替换</span></span><br><span class="line">	<span class="meta">@ConditionalOnSingleCandidate(RedisConnectionFactory.class)</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> RedisTemplate&lt;Object, Object&gt; <span class="title">redisTemplate</span><span class="params">(RedisConnectionFactory redisConnectionFactory)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 默认的RedisTemplate 没有过多的配置，对象都是需要序列化的</span></span><br><span class="line">        <span class="comment">// 两个泛型都是Object，Object的类型，后面使用需要强制转换&lt;String,Object&gt;</span></span><br><span class="line">		RedisTemplate&lt;Object, Object&gt; template = <span class="keyword">new</span> RedisTemplate&lt;&gt;();</span><br><span class="line">		template.setConnectionFactory(redisConnectionFactory);</span><br><span class="line">		<span class="keyword">return</span> template;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Bean</span></span><br><span class="line">	<span class="meta">@ConditionalOnMissingBean</span> <span class="comment">// 由于String是redis中最常使用的类型，所有单独提出来一个bean</span></span><br><span class="line">	<span class="meta">@ConditionalOnSingleCandidate(RedisConnectionFactory.class)</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> StringRedisTemplate <span class="title">stringRedisTemplate</span><span class="params">(RedisConnectionFactory redisConnectionFactory)</span> </span>&#123;</span><br><span class="line">		StringRedisTemplate template = <span class="keyword">new</span> StringRedisTemplate();</span><br><span class="line">		template.setConnectionFactory(redisConnectionFactory);</span><br><span class="line">		<span class="keyword">return</span> template;</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>



<blockquote>
<p>测试</p>
</blockquote>
<p>1.导入依赖</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-data-redis<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>2.配置连接</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">redis:</span></span><br><span class="line">    <span class="attr">host:</span> <span class="number">192.168</span><span class="number">.227</span><span class="number">.20</span></span><br><span class="line">    <span class="attr">port:</span> <span class="number">6379</span></span><br></pre></td></tr></table></figure>

<p>3.测试操作</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootTest</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Redis02SpringbootApplicationTests</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> StringRedisTemplate redisTemplate;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">contextLoads</span><span class="params">()</span> <span class="keyword">throws</span> JSONException </span>&#123;</span><br><span class="line">        <span class="comment">//redisTemplate</span></span><br><span class="line">        <span class="comment">//opsForValue 操作字符串  类似String</span></span><br><span class="line">        <span class="comment">//opsForList 操作list 类似list</span></span><br><span class="line">        <span class="comment">//opsForSet</span></span><br><span class="line">        <span class="comment">//opsForHash</span></span><br><span class="line">        ListOperations listOperations = redisTemplate.opsForList();</span><br><span class="line">        listOperations.leftPush(<span class="string">&quot;k1&quot;</span>,<span class="string">&quot;0010&quot;</span>);</span><br><span class="line">        <span class="comment">//获取redis的连接对象</span></span><br><span class="line">        RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();</span><br><span class="line">        connection.flushDb();</span><br><span class="line"></span><br><span class="line">        redisTemplate.opsForValue().set(<span class="string">&quot;mykey&quot;</span>,<span class="string">&quot;八极元&quot;</span>);</span><br><span class="line">        System.out.println(redisTemplate.opsForValue().get(<span class="string">&quot;mykey&quot;</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111172235268.png" alt="image-20211117223335541"></p>
<p><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111172238975.png" alt="image-20211117223810547"></p>
<blockquote>
<p>自定义RedisTemplate</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RedisConfig</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="meta">@SuppressWarnings(value=&quot;all&quot;)</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> RedisTemplate&lt;Object, Object&gt; <span class="title">redisTemplate</span><span class="params">(RedisConnectionFactory redisConnectionFactory)</span> </span>&#123;</span><br><span class="line">        RedisTemplate&lt;Object, Object&gt; template = <span class="keyword">new</span> RedisTemplate&lt;&gt;();</span><br><span class="line">        template.setConnectionFactory(redisConnectionFactory);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//Json序列化配置</span></span><br><span class="line">        Jackson2JsonRedisSerializer&lt;Object&gt; objectJackson2JsonRedisSerializer = <span class="keyword">new</span> Jackson2JsonRedisSerializer&lt;Object&gt;(Object.class);</span><br><span class="line">        ObjectMapper om = <span class="keyword">new</span> ObjectMapper();</span><br><span class="line">        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);</span><br><span class="line">        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);</span><br><span class="line">        objectJackson2JsonRedisSerializer.setObjectMapper(om);</span><br><span class="line">        <span class="comment">//String的序列化</span></span><br><span class="line">        StringRedisSerializer stringRedisSerializer = <span class="keyword">new</span> StringRedisSerializer();</span><br><span class="line">        <span class="comment">//配置具体的序列化方式</span></span><br><span class="line">        template.setKeySerializer(objectJackson2JsonRedisSerializer);</span><br><span class="line">        template.setHashKeySerializer(objectJackson2JsonRedisSerializer);</span><br><span class="line">        template.setValueSerializer(objectJackson2JsonRedisSerializer);</span><br><span class="line">        template.setHashValueSerializer(objectJackson2JsonRedisSerializer);</span><br><span class="line">        template.afterPropertiesSet();</span><br><span class="line">        <span class="keyword">return</span> template;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>单位</p>
</blockquote>
<p><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111181153689.png" alt="image-20211118115326120"></p>
<p>1.配置文件unit单位对大小写不敏感</p>
<blockquote>
<p>包含</p>
</blockquote>
<p><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111181157270.png" alt="image-20211118115737905"></p>
<p>可以将多个配置文件导在一起</p>
<blockquote>
<p>网络</p>
</blockquote>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">bind</span> 127.0.0.1  <span class="comment">#绑定的ip</span></span><br><span class="line">protected-mode yes  <span class="comment"># 保护模式</span></span><br><span class="line">port 6379  <span class="comment"># 端口设置</span></span><br></pre></td></tr></table></figure>

<blockquote>
<p>通用的GEBNERAL</p>
</blockquote>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">daemonize yes <span class="comment"># 以守护进程的方式允许，默认是no，需要自己开启yes（后台方式运行）</span></span><br><span class="line"></span><br><span class="line">pidfile /var/run/redis_6379.pid  <span class="comment"># 如果以后台方式运行，就需要指定一个pid进程文件</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#日志</span></span><br><span class="line"><span class="comment"># Specify the server verbosity level.</span></span><br><span class="line"> 292 <span class="comment"># This can be one of:</span></span><br><span class="line"> 293 <span class="comment"># debug (a lot of information, useful for development/testing)</span></span><br><span class="line"> 294 <span class="comment"># verbose (many rarely useful info, but not a mess like the debug level)</span></span><br><span class="line"> 295 <span class="comment"># notice (moderately verbose, what you want in production probably)</span></span><br><span class="line"> 296 <span class="comment"># warning (only very important / critical messages are logged)</span></span><br><span class="line"> 297 loglevel notice</span><br><span class="line"> </span><br><span class="line">logfile <span class="string">&quot;&quot;</span> <span class="comment">#日志的文件位置名</span></span><br><span class="line">databases 16  <span class="comment">#数据库的数量，默认是16个数据库</span></span><br><span class="line">always-show-logo yes  <span class="comment"># 是否总是显示LOGO</span></span><br></pre></td></tr></table></figure>

<blockquote>
<p>快照</p>
</blockquote>
<p>持久化，在规定的时间内，执行了多少次操作，则会持久化到文件.rdb .aof</p>
<p>redis是内存数据库，如果没有持久化，那么数据断电即失！</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#如果3600秒内，至少有一个key进行了修改，就会进行持久化操作</span></span><br><span class="line">save 3600 1</span><br><span class="line">save 300 100</span><br><span class="line">save 60 10000</span><br><span class="line"></span><br><span class="line">stop-writes-on-bgsave-error yes  <span class="comment"># 持久化如果出错，是否需要继续工作！</span></span><br><span class="line"></span><br><span class="line">rdbcompression yes  <span class="comment"># 是否压缩.rdb文件，需要消耗一些cpu资源</span></span><br><span class="line"></span><br><span class="line">dir ./  <span class="comment"># rdb 文件保存的目录</span></span><br></pre></td></tr></table></figure>

<blockquote>
<p>REPLICATION复制，主从复制</p>
</blockquote>
<blockquote>
<p>SECURITY 安全</p>
</blockquote>
<p>可以设置redis的密码，默认是没有密码</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; ping</span><br><span class="line">PONG</span><br><span class="line">127.0.0.1:6379&gt; config get requirepass  <span class="comment"># 获取redis的密码</span></span><br><span class="line">1) <span class="string">&quot;requirepass&quot;</span></span><br><span class="line">2) <span class="string">&quot;&quot;</span></span><br><span class="line">127.0.0.1:6379&gt; config <span class="built_in">set</span> requirepass <span class="string">&quot;123456&quot;</span>  <span class="comment"># 设置redis的密码</span></span><br><span class="line">OK</span><br><span class="line">127.0.0.1:6379&gt; config get requirepass</span><br><span class="line">1) <span class="string">&quot;requirepass&quot;</span></span><br><span class="line">2) <span class="string">&quot;123456&quot;</span></span><br><span class="line">127.0.0.1:6379&gt; auth 123456  <span class="comment"># 使用密码进行登录</span></span><br><span class="line">OK</span><br></pre></td></tr></table></figure>

<blockquote>
<p>限制CLIENTS</p>
</blockquote>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">maxclients 10000  <span class="comment"># 设置能连接上redis的最大客户端数量</span></span><br><span class="line">maxmemory &lt;bytes&gt;  <span class="comment"># redis 配置最大内存容量</span></span><br><span class="line">maxmemory-policy noeviction  <span class="comment"># 内存达到最大值之后的处理策略</span></span><br><span class="line"></span><br><span class="line">1.volative-lru: 只对设置了过期时间的key进行LRU（默认值）</span><br><span class="line">2.allkeys-lru: 删除lru算法的key</span><br><span class="line">3.volatile-random: 随机删除即将过期key</span><br><span class="line">4.allkeys-random: 随机删除</span><br><span class="line">5.volatile-ttl: 删除即将过期的</span><br><span class="line">6.noeviction: 永不过期，返回错误</span><br></pre></td></tr></table></figure>

<blockquote>
<p>APPEND ONLY 模式  aof配置</p>
</blockquote>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">appendonly no  <span class="comment"># 默认不开启aof模式，默认使用rdb方式持久化，大部分情况下rdb完全够用</span></span><br><span class="line">appendfilename <span class="string">&quot;appendonly.aof&quot;</span>  <span class="comment"># 持久化的文件的名字</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># appendfsync always  # 每秒修改都会 sync，消耗性能</span></span><br><span class="line">appendfsync everysec  <span class="comment"># 每秒执行一次 sync，可能会丢失这1s的数据</span></span><br><span class="line"><span class="comment"># appendfsync no      # 不执行 sync，这个时候操作系统自己同步数据，速度最快</span></span><br></pre></td></tr></table></figure>

<h1 id="四、Redis持久化操作"><a href="#四、Redis持久化操作" class="headerlink" title="四、Redis持久化操作"></a>四、Redis持久化操作</h1><p>Redis 提供了2个不同形式的持久化方式。</p>
<ol>
<li>RDB（Redis DataBase）</li>
<li>AOF（Append Of File）</li>
</ol>
<h2 id="1-RDB（Redis-DataBase）"><a href="#1-RDB（Redis-DataBase）" class="headerlink" title="1.RDB（Redis DataBase）"></a>1.RDB（Redis DataBase）</h2><h3 id="1-1-RDB是什么"><a href="#1-1-RDB是什么" class="headerlink" title="1.1 RDB是什么"></a>1.1 RDB是什么</h3><p>在指定的<strong>时间间隔</strong>内将内存中的数据集<strong>快照</strong>写入磁盘， 也就是行话讲的Snapshot快照，它恢复时是将快照文件直接读到内存里</p>
<h3 id="1-2-备份操作是如何进行的"><a href="#1-2-备份操作是如何进行的" class="headerlink" title="1.2 备份操作是如何进行的"></a>1.2 备份操作是如何进行的</h3><p>Redis会单独创建（fork）一个子进程来进行持久化，会<strong>先将数据写入到 一个临时文件中</strong>，待持久化过程都结束了，再用这个<strong>临时文件替换上次持久化</strong>好的文件。 整个过程中，主进程是不进行任何IO操作的，这就确保了极高的性能 如果需要进行大规模数据的恢复，且对于数据恢复的完整性不是非常敏感，那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。</p>
<h3 id="1-3-Fork"><a href="#1-3-Fork" class="headerlink" title="1.3 Fork"></a>1.3 Fork</h3><ul>
<li>Fork的作用是复制一个与当前进程一样的进程。新进程的所有数据（变量、环境变量、程序计数器等） 数值都和原进程一致，但是是一个全新的进程，并<strong>作为原进程的子进程</strong></li>
<li>在Linux程序中，fork()会产生一个和父进程完全相同的子进程，但子进程在此后多会exec系统调用，出于效率考虑，Linux中引入了 “<strong>写时复制技术</strong>”</li>
<li><strong>一般情况父进程和子进程会共用同一段物理内存</strong>，只有进程空间的各段的内容要发生变化时，才会将父进程的内容复制一份给子进程。</li>
</ul>
<h3 id="1-4-RDB持久化流程"><a href="#1-4-RDB持久化流程" class="headerlink" title="1.4 RDB持久化流程"></a>1.4 RDB持久化流程</h3><p><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111181419794.png" alt="在这里插入图片描述"></p>
<h3 id="1-5-dump-rdb文件"><a href="#1-5-dump-rdb文件" class="headerlink" title="1.5 dump.rdb文件"></a>1.5 dump.rdb文件</h3><p>在redis.conf中配置文件名称，默认为dump.rdb<br><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111181429920.png" alt="image-20211118142919631"></p>
<blockquote>
<p>触发机制</p>
</blockquote>
<p>1.save的规则满足的情况下，会自动触发rdb规则</p>
<p>2.执行flushall命令，也会触发rdb规则</p>
<p>3.推出redis，也会产生rdb文件</p>
<h3 id="1-6-配置位置"><a href="#1-6-配置位置" class="headerlink" title="1.6 配置位置"></a>1.6 配置位置</h3><p>rdb文件的保存路径，也可以修改。<em><strong>默认为Redis启动时命令行所在的目录下</strong></em></p>
<p>dir “/myredis/”<br><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111181430315.png" alt="image-20211118143026453"></p>
<blockquote>
<p>如何恢复rdb文件</p>
</blockquote>
<p>只需要将rdb文件放在redis启动目录下即可，redis启动的时候会检查dump。rdb恢复其中的数据</p>
<p><strong>优点：</strong></p>
<ol>
<li>适合大规模的数据恢复</li>
<li>对数据完整性和一致性要求不高更适合使用</li>
<li>节省磁盘空间</li>
<li>恢复速度快</li>
</ol>
<p><strong>缺点：</strong></p>
<ol>
<li>Fork的时候，内存中的数据被克隆了一份，大致2倍的膨胀性需要考虑</li>
<li>虽然Redis在fork时使用了<strong>写时拷贝技术</strong>,但是如果数据庞大时还是比较消耗性能。</li>
<li>在备份周期在一定间隔时间做一次备份，所以如果Redis意外down掉的话，就会丢失最后一次快照后的所有修改。</li>
</ol>
<h3 id="1-7如何停止RDB"><a href="#1-7如何停止RDB" class="headerlink" title="1.7如何停止RDB"></a>1.7如何停止RDB</h3><p>动态停止RDB：redis-cli config set save “”#save后给空值，表示禁用保存策略</p>
<h2 id="2-AOF（Append-Only-File）"><a href="#2-AOF（Append-Only-File）" class="headerlink" title="2.AOF（Append Only File）"></a>2.AOF（Append Only File）</h2><h3 id="2-1-AOF是什么"><a href="#2-1-AOF是什么" class="headerlink" title="2.1 AOF是什么"></a>2.1 AOF是什么</h3><p><strong>以日志的形式来记录每个写操作（增量保存）</strong>，将Redis执行过的所有写指令记录下来(<strong>读操作不记录</strong>)， <strong>只许追加文件但不可以改写文件</strong>，redis启动之初会读取该文件重新构建数据，换言之，redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作</p>
<h3 id="2-2-AOF持久化流程"><a href="#2-2-AOF持久化流程" class="headerlink" title="2.2 AOF持久化流程"></a>2.2 AOF持久化流程</h3><ol>
<li>客户端的请求写命令会被append追加到AOF缓冲区内；</li>
<li>AOF缓冲区根据AOF持久化策略[always,everysec,no]将操作sync同步到磁盘的AOF文件中；</li>
<li>AOF文件大小超过重写策略或手动重写时，会对AOF文件rewrite重写，压缩AOF文件容量；</li>
<li>Redis服务重启时，会重新load加载AOF文件中的写操作达到数据恢复的目的；</li>
</ol>
<p><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111181443794.png" alt="在这里插入图片描述"></p>
<h3 id="2-3-AOF默认不开启"><a href="#2-3-AOF默认不开启" class="headerlink" title="2.3 AOF默认不开启"></a>2.3 AOF默认不开启</h3><p>可以在redis.conf中配置文件名称，默认为 <strong>appendonly.aof</strong><br>AOF文件的保存路径，同RDB的路径一致。</p>
<h3 id="2-4-AOF和RDB同时开启，redis听谁的？"><a href="#2-4-AOF和RDB同时开启，redis听谁的？" class="headerlink" title="2.4 AOF和RDB同时开启，redis听谁的？"></a>2.4 AOF和RDB同时开启，redis听谁的？</h3><p>AOF和RDB同时开启，系统默认取AOF的数据（数据不会存在丢失）</p>
<h3 id="2-5-AOF启动-修复-恢复"><a href="#2-5-AOF启动-修复-恢复" class="headerlink" title="2.5 AOF启动/修复/恢复"></a>2.5 AOF启动/修复/恢复</h3><ul>
<li>AOF的备份机制和性能虽然和RDB不同, 但是备份和恢复的操作同RDB一样，都是拷贝备份文件，需要恢复时再拷贝到Redis工作目录下，启动系统即加载。</li>
<li>正常恢复<br>1.修改默认的appendonly no，改为yes<br>2.将有数据的aof文件复制一份保存到对应目录(查看目录：config get dir)<br>3.恢复：重启redis然后重新加载</li>
<li>异常恢复<br>1.修改默认的appendonly no，改为yes<br>2.如遇到AOF文件损坏，通过/usr/local/bin/redis-check-aof–fix appendonly.aof进行恢复<br>3.备份被写坏的AOF文件<br>4.恢复：重启redis，然后重新加载</li>
</ul>
<h3 id="2-6-AOF同步频率设置"><a href="#2-6-AOF同步频率设置" class="headerlink" title="2.6 AOF同步频率设置"></a>2.6 AOF同步频率设置</h3><p>appendfsync always<br>始终同步，每次Redis的写入都会立刻记入日志；性能较差但数据完整性比较好</p>
<hr>
<p>appendfsync everysec<br>每秒同步，每秒记入日志一次，如果宕机，本秒的数据可能丢失。</p>
<hr>
<p>appendfsync no<br>redis不主动进行同步，把同步时机交给操作系统</p>
<blockquote>
<p>重写规则</p>
</blockquote>
<p><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111181501432.png" alt="image-20211118150114983"></p>
<p>如果aof文件大于64m。fork一个新的进程来将文件进行重写！</p>
<blockquote>
<p>优点和缺点</p>
</blockquote>
<p>优点：</p>
<p>1.每一次修改都同步，文件的完整性会更加好</p>
<p>2.每秒同步一次，可能会丢失一秒的数据</p>
<p>3.从不同步，效率最高的！</p>
<p>缺点：</p>
<p>1.相对于数据文件来说，aof远远大于rdb，修改的速度也比rdb慢</p>
<p>2.AOF运行效率也要比rdb慢，所以redis默认配置的是rdb</p>
<h1 id="五、Redis的发布和订阅"><a href="#五、Redis的发布和订阅" class="headerlink" title="五、Redis的发布和订阅"></a>五、Redis的发布和订阅</h1><h2 id="1-什么是发布和订阅"><a href="#1-什么是发布和订阅" class="headerlink" title="1.什么是发布和订阅"></a>1.什么是发布和订阅</h2><p>Redis 发布订阅 (pub/sub) 是一种消息通信模式：发送者 (pub) 发送消息，订阅者 (sub) 接收消息。</p>
<p>Redis 客户端可以订阅任意数量的频道。</p>
<h2 id="2-Redis的发布和订阅"><a href="#2-Redis的发布和订阅" class="headerlink" title="2.Redis的发布和订阅"></a>2.Redis的发布和订阅</h2><ol>
<li><p>客户端可以订阅频道如下图<br><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111181517562.png" alt="img"></p>
</li>
<li><p>当有新消息通过 PUBLISH 命令发送给频道 channel1 时， 这个消息就会被发送给订阅它的三个客户端：</p>
<p><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111181517032.png" alt="img"></p>
</li>
</ol>
<h2 id="3-Redis-发布订阅命令"><a href="#3-Redis-发布订阅命令" class="headerlink" title="3.Redis 发布订阅命令"></a>3.Redis 发布订阅命令</h2><p>下表列出了 redis 发布订阅常用命令：</p>
<table>
<thead>
<tr>
<th align="left">序号</th>
<th align="left">命令及描述</th>
</tr>
</thead>
<tbody><tr>
<td align="left">1</td>
<td align="left">[PSUBSCRIBE pattern <a target="_blank" rel="noopener" href="https://www.runoob.com/redis/pub-sub-psubscribe.html">pattern …]</a> 订阅一个或多个符合给定模式的频道。</td>
</tr>
<tr>
<td align="left">2</td>
<td align="left">[PUBSUB subcommand <a target="_blank" rel="noopener" href="https://www.runoob.com/redis/pub-sub-pubsub.html">argument [argument …]]</a> 查看订阅与发布系统状态。</td>
</tr>
<tr>
<td align="left">3</td>
<td align="left"><a target="_blank" rel="noopener" href="https://www.runoob.com/redis/pub-sub-publish.html">PUBLISH channel message</a> 将信息发送到指定的频道。</td>
</tr>
<tr>
<td align="left">4</td>
<td align="left">[PUNSUBSCRIBE <a target="_blank" rel="noopener" href="https://www.runoob.com/redis/pub-sub-punsubscribe.html">pattern [pattern …]]</a> 退订所有给定模式的频道。</td>
</tr>
<tr>
<td align="left">5</td>
<td align="left">[SUBSCRIBE channel <a target="_blank" rel="noopener" href="https://www.runoob.com/redis/pub-sub-subscribe.html">channel …]</a> 订阅给定的一个或多个频道的信息。</td>
</tr>
<tr>
<td align="left">6</td>
<td align="left">[UNSUBSCRIBE <a target="_blank" rel="noopener" href="https://www.runoob.com/redis/pub-sub-unsubscribe.html">channel [channel …]]</a> 指退订给定的频道。</td>
</tr>
</tbody></table>
<h2 id="4-发布订阅命令行实现"><a href="#4-发布订阅命令行实现" class="headerlink" title="4.发布订阅命令行实现"></a>4.发布订阅命令行实现</h2><ol>
<li><p>打开一个客户端订阅channel1<br>SUBSCRIBE channel1</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; subscribe yuanyi</span><br><span class="line">Reading messages... (press Ctrl-C to quit)</span><br><span class="line">1) <span class="string">&quot;subscribe&quot;</span></span><br><span class="line">2) <span class="string">&quot;yuanyi&quot;</span></span><br><span class="line">3) (<span class="built_in">integer</span>) 1</span><br><span class="line"></span><br><span class="line"><span class="comment">#等待推送的信息</span></span><br><span class="line">1) <span class="string">&quot;message&quot;</span></span><br><span class="line">2) <span class="string">&quot;yuanyi&quot;</span></span><br><span class="line">3) <span class="string">&quot;helloworld&quot;</span></span><br><span class="line">1) <span class="string">&quot;message&quot;</span></span><br><span class="line">2) <span class="string">&quot;yuanyi&quot;</span></span><br><span class="line">3) <span class="string">&quot;hi</span></span><br></pre></td></tr></table></figure></li>
<li><p>发送端，给channel1发布消息hello<br>publish channel1 hello</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; publish yuanyi <span class="string">&quot;helloworld&quot;</span></span><br><span class="line">(<span class="built_in">integer</span>) 1</span><br><span class="line">127.0.0.1:6379&gt; publish yuanyi <span class="string">&quot;hi&quot;</span></span><br><span class="line">(<span class="built_in">integer</span>) 1</span><br></pre></td></tr></table></figure>

<p>返回的1是订阅者数量<br><strong>注</strong>：发布的消息没有持久化，如果在订阅的客户端收不到hello，只能收到订阅后发布的消息</p>
</li>
</ol>
<blockquote>
<p>使用场景</p>
</blockquote>
<ol>
<li>实时消息系统！</li>
<li>实时聊天！（频道当作聊天室，将信息回显给所有人即可）</li>
<li>订阅，关注系统</li>
<li>稍微复杂的场景就使用消息中间件MQ</li>
</ol>
<h1 id="六、Redis主从复制"><a href="#六、Redis主从复制" class="headerlink" title="六、Redis主从复制"></a>六、Redis主从复制</h1><h2 id="1-主从复制是什么"><a href="#1-主从复制是什么" class="headerlink" title="1.主从复制是什么"></a>1.主从复制是什么</h2><p>主机数据更新后根据配置和策略， 自动同步到备机的master/slaver机制，Master以写为主，Slave以读为主</p>
<h2 id="2-能干嘛"><a href="#2-能干嘛" class="headerlink" title="2.能干嘛"></a>2.能干嘛</h2><ul>
<li>读写分离，性能扩展</li>
<li>容灾快速恢复</li>
</ul>
<p><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111202154840.png" alt="在这里插入图片描述"></p>
<h2 id="3-具体操作：主从复制"><a href="#3-具体操作：主从复制" class="headerlink" title="3.具体操作：主从复制"></a>3.具体操作：主从复制</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">拷贝多个redis.conf文件include(写绝对路径)</span><br><span class="line">开启daemonize yes</span><br><span class="line">Pid文件名字pidfile</span><br><span class="line">指定端口port</span><br><span class="line">Log文件名字</span><br><span class="line">dump.rdb名字dbfilename</span><br><span class="line">Appendonly 关掉或者换名字</span><br></pre></td></tr></table></figure>

<h3 id="3-1新建redis79-conf、redis80-conf、redis81-conf"><a href="#3-1新建redis79-conf、redis80-conf、redis81-conf" class="headerlink" title="3.1新建redis79.conf、redis80.conf、redis81.conf"></a>3.1新建redis79.conf、redis80.conf、redis81.conf</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">include /usr/<span class="built_in">local</span>/bin/Redisconfig/redis.conf</span><br><span class="line">pidfile <span class="string">&quot;/var/run/redis_6379.pid&quot;</span></span><br><span class="line">port 6379</span><br><span class="line">dbfilename <span class="string">&quot;dump6379.rdb&quot;</span></span><br><span class="line">logfile <span class="string">&quot;6379.log&quot;</span></span><br></pre></td></tr></table></figure>

<p>同理。。。</p>
<p>slave-priority 10</p>
<p>设置从机的优先级，值越小，优先级越高，用于选举主机时使用。默认100</p>
<h3 id="3-2-查看三台主机运行情况"><a href="#3-2-查看三台主机运行情况" class="headerlink" title="3.2 查看三台主机运行情况"></a>3.2 查看三台主机运行情况</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">info replication</span><br></pre></td></tr></table></figure>

<h3 id="3-3-配从-库-不配主-库"><a href="#3-3-配从-库-不配主-库" class="headerlink" title="3.3 配从(库)不配主(库)"></a>3.3 配从(库)不配主(库)</h3><p>slaveof ip port<br>成为某个实例的从服务器</p>
<h2 id="4-常用三招"><a href="#4-常用三招" class="headerlink" title="4.常用三招"></a>4.常用三招</h2><h3 id="4-1一主二仆"><a href="#4-1一主二仆" class="headerlink" title="4.1一主二仆"></a>4.1一主二仆</h3><ul>
<li>从机可读不可写</li>
</ul>
<p><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111202206581.png" alt="image-20211120220651467"></p>
<ul>
<li>主机shutdown之后，从机不会上位</li>
<li>主机回来之后，主机新增记录，从机还能顺利复制</li>
<li>从机宕机之后，从机会单独变为一台主机</li>
</ul>
<p><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111202234073.png" alt="img"></p>
<h3 id="4-2-薪火相传"><a href="#4-2-薪火相传" class="headerlink" title="4.2 薪火相传"></a>4.2 薪火相传</h3><p>上一个Slave可以是下一个slave的Master，Slave同样可以接收其他 slaves的连接和同步请求，那么该slave作为了链条中下一个的master, 可以有效减轻master的写压力,去中心化降低风险。</p>
<p>用 slaveof ip port<br>中途变更转向:会清除之前的数据，重新建立拷贝最新的<br>风险是一旦某个slave宕机，后面的slave都没法备份<br>主机挂了，从机还是从机，无法写数据了</p>
<p><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111202234826.png" alt="在这里插入图片描述"></p>
<p><strong>当中间的从机宕机后又恢复时，他会断开与主机之间的关系</strong></p>
<p>注意这个时候虽然中间的从机即使主机又是从机，但是它也只能进行读的操作</p>
<h3 id="4-3-反客为主"><a href="#4-3-反客为主" class="headerlink" title="4.3 反客为主"></a>4.3 反客为主</h3><p>当一个master宕机后，后面的slave可以立刻升为master，其后面的slave不用做任何修改。</p>
<p>用 slaveof no one 将从机变为主机。</p>
<h2 id="5-复制原理"><a href="#5-复制原理" class="headerlink" title="5.复制原理"></a>5.复制原理</h2><ul>
<li>Slave启动成功连接到master后会发送一个sync命令</li>
<li>Master接到命令启动后台的存盘进程，同时收集所有接收到的用于修改数据集命令， 在后台进程执行完毕之后，master将传送整个数据文件到slave,以完成一次完全同步</li>
<li>全量复制：而slave服务在接收到数据库文件数据后，将其存盘并加载到内存中。</li>
<li>增量复制：Master继续将新的所有收集到的修改命令依次传给slave,完成同步</li>
<li>但是只要是重新连接master,一次完全同步（全量复制)将被自动执行</li>
</ul>
<p><img src="https://gitee.com/RMByuan/note-material/raw/master/studyImg/202111202242156.png" alt="在这里插入图片描述"></p>
<h2 id="哨兵模式"><a href="#哨兵模式" class="headerlink" title="哨兵模式"></a>哨兵模式</h2><p>（自动选举老大的模式）</p>
<p>1.配置哨兵配置文件</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># sentinel monitor 被监控的名称 host port 1</span></span><br><span class="line">sentinel monitor myredis 127.0.0.1 6379 1 </span><br></pre></td></tr></table></figure>

<p>后面的数字1代表主机宕机后，从机投票来接替主机</p>
<p>2.启动哨兵</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">redis-sentinel 哨兵配置文件</span><br></pre></td></tr></table></figure>

<blockquote>
<p>哨兵模式</p>
</blockquote>
<p>优点：</p>
<ol>
<li>哨兵集群，基于主从复制模式，所有的主从配置优点，它全有</li>
<li>主从可以切换，故障可以转移，系统的可用性会更好</li>
<li>哨兵模式就是主从模式的升级，手动到自动，更加健壮</li>
</ol>
<p>缺点：</p>
<ol>
<li>Redis不好在线扩容，集群容量一旦达到上限，在线扩容十分麻烦</li>
<li>实现哨兵模式的配置十分麻烦</li>
</ol>
 
      <!-- reward -->
      
      <div id="reword-out">
        <div id="reward-btn">
          打赏
        </div>
      </div>
      
    </div>
    

    <!-- copyright -->
    
    <div class="declare">
      <ul class="post-copyright">
        <li>
          <i class="ri-copyright-line"></i>
          <strong>版权声明： </strong>
          
          本博客所有文章除特别声明外，著作权归作者所有。转载请注明出处！
          
        </li>
      </ul>
    </div>
    
    <footer class="article-footer">
       
<div class="share-btn">
      <span class="share-sns share-outer">
        <i class="ri-share-forward-line"></i>
        分享
      </span>
      <div class="share-wrap">
        <i class="arrow"></i>
        <div class="share-icons">
          
          <a class="weibo share-sns" href="javascript:;" data-type="weibo">
            <i class="ri-weibo-fill"></i>
          </a>
          <a class="weixin share-sns wxFab" href="javascript:;" data-type="weixin">
            <i class="ri-wechat-fill"></i>
          </a>
          <a class="qq share-sns" href="javascript:;" data-type="qq">
            <i class="ri-qq-fill"></i>
          </a>
          <a class="douban share-sns" href="javascript:;" data-type="douban">
            <i class="ri-douban-line"></i>
          </a>
          <!-- <a class="qzone share-sns" href="javascript:;" data-type="qzone">
            <i class="icon icon-qzone"></i>
          </a> -->
          
          <a class="facebook share-sns" href="javascript:;" data-type="facebook">
            <i class="ri-facebook-circle-fill"></i>
          </a>
          <a class="twitter share-sns" href="javascript:;" data-type="twitter">
            <i class="ri-twitter-fill"></i>
          </a>
          <a class="google share-sns" href="javascript:;" data-type="google">
            <i class="ri-google-fill"></i>
          </a>
        </div>
      </div>
</div>

<div class="wx-share-modal">
    <a class="modal-close" href="javascript:;"><i class="ri-close-circle-line"></i></a>
    <p>扫一扫，分享到微信</p>
    <div class="wx-qrcode">
      <img src="//api.qrserver.com/v1/create-qr-code/?size=150x150&data=http://example.com/2021/12/04/Redis%E7%AC%94%E8%AE%B0/" alt="微信分享二维码">
    </div>
</div>

<div id="share-mask"></div>  
    </footer>
  </div>

   
  <nav class="article-nav">
    
      <a href="/2021/12/04/Dubbo%E7%AC%94%E8%AE%B0/" class="article-nav-link">
        <strong class="article-nav-caption">上一篇</strong>
        <div class="article-nav-title">
          
            Dubbo分布式架构
          
        </div>
      </a>
    
    
      <a href="/2021/12/04/%E5%AE%89%E5%85%A8%E4%B8%AD%E9%97%B4%E4%BB%B6/" class="article-nav-link">
        <strong class="article-nav-caption">下一篇</strong>
        <div class="article-nav-title">Spring Security</div>
      </a>
    
  </nav>

   
<!-- valine评论 -->
<div id="vcomments-box">
  <div id="vcomments"></div>
</div>
<script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/valine@1.4.14/dist/Valine.min.js"></script>
<script>
  new Valine({
    el: "#vcomments",
    app_id: "",
    app_key: "",
    path: window.location.pathname,
    avatar: "monsterid",
    placeholder: "给我的文章加点评论吧~",
    recordIP: true,
  });
  const infoEle = document.querySelector("#vcomments .info");
  if (infoEle && infoEle.childNodes && infoEle.childNodes.length > 0) {
    infoEle.childNodes.forEach(function (item) {
      item.parentNode.removeChild(item);
    });
  }
</script>
<style>
  #vcomments-box {
    padding: 5px 30px;
  }

  @media screen and (max-width: 800px) {
    #vcomments-box {
      padding: 5px 0px;
    }
  }

  #vcomments-box #vcomments {
    background-color: #fff;
  }

  .v .vlist .vcard .vh {
    padding-right: 20px;
  }

  .v .vlist .vcard {
    padding-left: 10px;
  }
</style>

 
   
     
</article>

</section>
      <footer class="footer">
  <div class="outer">
    <ul>
      <li>
        Copyrights &copy;
        2015-2022
        <i class="ri-heart-fill heart_icon"></i> Yuan
      </li>
    </ul>
    <ul>
      <li>
        
      </li>
    </ul>
    <ul>
      <li>
        
        
        <span>
  <span><i class="ri-user-3-fill"></i>访问人数:<span id="busuanzi_value_site_uv"></span></span>
  <span class="division">|</span>
  <span><i class="ri-eye-fill"></i>浏览次数:<span id="busuanzi_value_page_pv"></span></span>
</span>
        
      </li>
    </ul>
    <ul>
      
    </ul>
    <ul>
      
    </ul>
    <ul>
      <li>
        <!-- cnzz统计 -->
        
        <script type="text/javascript" src='https://s9.cnzz.com/z_stat.php?id=1278069914&amp;web_id=1278069914'></script>
        
      </li>
    </ul>
  </div>
</footer>    
    </main>
    <div class="float_btns">
      <div class="totop" id="totop">
  <i class="ri-arrow-up-line"></i>
</div>

<div class="todark" id="todark">
  <i class="ri-moon-line"></i>
</div>

    </div>
    <aside class="sidebar on">
      <button class="navbar-toggle"></button>
<nav class="navbar">
  
  <div class="logo">
    <a href="/"><img src="/images/ayer-side.svg" alt="MyBlog"></a>
  </div>
  
  <ul class="nav nav-main">
    
    <li class="nav-item">
      <a class="nav-item-link" href="/">主页</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/archives">归档</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/categories">分类</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags">标签</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/friends">友链</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/about">关于我</a>
    </li>
    
  </ul>
</nav>
<nav class="navbar navbar-bottom">
  <ul class="nav">
    <li class="nav-item">
      
      <a class="nav-item-link nav-item-search"  title="搜索">
        <i class="ri-search-line"></i>
      </a>
      
      
      <a class="nav-item-link" target="_blank" href="/atom.xml" title="RSS Feed">
        <i class="ri-rss-line"></i>
      </a>
      
    </li>
  </ul>
</nav>
<div class="search-form-wrap">
  <div class="local-search local-search-plugin">
  <input type="search" id="local-search-input" class="local-search-input" placeholder="Search...">
  <div id="local-search-result" class="local-search-result"></div>
</div>
</div>
    </aside>
    <div id="mask"></div>

<!-- #reward -->
<div id="reward">
  <span class="close"><i class="ri-close-line"></i></span>
  <p class="reward-p"><i class="ri-cup-line"></i>请我喝杯咖啡吧~</p>
  <div class="reward-box">
    
    
  </div>
</div>
    
<script src="/js/jquery-3.6.0.min.js"></script>
 
<script src="/js/lazyload.min.js"></script>

<!-- Tocbot -->
 
<script src="/js/tocbot.min.js"></script>

<script>
  tocbot.init({
    tocSelector: ".tocbot",
    contentSelector: ".article-entry",
    headingSelector: "h1, h2, h3, h4, h5, h6",
    hasInnerContainers: true,
    scrollSmooth: true,
    scrollContainer: "main",
    positionFixedSelector: ".tocbot",
    positionFixedClass: "is-position-fixed",
    fixedSidebarOffset: "auto",
  });
</script>

<script src="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.js"></script>
<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.css"
/>
<script src="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/js/jquery.justifiedGallery.min.js"></script>

<script src="/dist/main.js"></script>

<!-- ImageViewer -->
 <!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                        <div class="pswp__preloader__cut">
                            <div class="pswp__preloader__donut"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div>
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.min.css">
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js"></script>

<script>
    function viewer_init() {
        let pswpElement = document.querySelectorAll('.pswp')[0];
        let $imgArr = document.querySelectorAll(('.article-entry img:not(.reward-img)'))

        $imgArr.forEach(($em, i) => {
            $em.onclick = () => {
                // slider展开状态
                // todo: 这样不好，后面改成状态
                if (document.querySelector('.left-col.show')) return
                let items = []
                $imgArr.forEach(($em2, i2) => {
                    let img = $em2.getAttribute('data-idx', i2)
                    let src = $em2.getAttribute('data-target') || $em2.getAttribute('src')
                    let title = $em2.getAttribute('alt')
                    // 获得原图尺寸
                    const image = new Image()
                    image.src = src
                    items.push({
                        src: src,
                        w: image.width || $em2.width,
                        h: image.height || $em2.height,
                        title: title
                    })
                })
                var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, {
                    index: parseInt(i)
                });
                gallery.init()
            }
        })
    }
    viewer_init()
</script> 
<!-- MathJax -->

<!-- Katex -->

<!-- busuanzi  -->
 
<script src="/js/busuanzi-2.3.pure.min.js"></script>
 
<!-- ClickLove -->
 
<script src="/js/clickLove.js"></script>
 
<!-- ClickBoom1 -->

<!-- ClickBoom2 -->

<!-- CodeCopy -->
 
<link rel="stylesheet" href="/css/clipboard.css">
 <script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<script>
  function wait(callback, seconds) {
    var timelag = null;
    timelag = window.setTimeout(callback, seconds);
  }
  !function (e, t, a) {
    var initCopyCode = function(){
      var copyHtml = '';
      copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
      copyHtml += '<i class="ri-file-copy-2-line"></i><span>COPY</span>';
      copyHtml += '</button>';
      $(".highlight .code pre").before(copyHtml);
      $(".article pre code").before(copyHtml);
      var clipboard = new ClipboardJS('.btn-copy', {
        target: function(trigger) {
          return trigger.nextElementSibling;
        }
      });
      clipboard.on('success', function(e) {
        let $btn = $(e.trigger);
        $btn.addClass('copied');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-checkbox-circle-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPIED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-checkbox-circle-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
      clipboard.on('error', function(e) {
        e.clearSelection();
        let $btn = $(e.trigger);
        $btn.addClass('copy-failed');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-time-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPY FAILED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-time-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
    }
    initCopyCode();
  }(window, document);
</script>
 
<!-- CanvasBackground -->

<script>
  if (window.mermaid) {
    mermaid.initialize({ theme: "forest" });
  }
</script>


    
    <div id="music">
    
    
    
    <iframe frameborder="no" border="1" marginwidth="0" marginheight="0" width="200" height="52"
        src="//music.163.com/outchain/player?type=2&id=22707008&auto=1&height=32"></iframe>
</div>

<style>
    #music {
        position: fixed;
        right: 15px;
        bottom: 0;
        z-index: 998;
    }
</style>
    
    

  </div>
</body>

</html>